/* 2021-02-05 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <string.h>

// 消息队列结构
struct msg_from { 
    long mtype;
    char mtext;
};

// 联合体，用于semctl初始化
union semun {
    int              val; /* for SETVAL */
    struct semid_ds *buf;
    unsigned short  *array;
};


// P操作
//    若信号量值为1，获取资源并将信号量值-1
//    若信号量值为0，进程挂起等待
int sem_p(int sem_id) {
    struct sembuf sbuf;
    sbuf.sem_num = 0; // 序号
    sbuf.sem_op = -1; // P操作
    sbuf.sem_flg = SEM_UNDO;

    if (semop(sem_id, &sbuf, 1) == -1) {
        perror("P operation Error");
        return -1;
    }
    return 0;
}

// V操作
//    释放资源将信号量值+1
//    如果有进程正在挂起等待，则唤醒它们
int sem_v(int sem_id) {
    struct sembuf sbuf;
    sbuf.sem_num = 0; // 序号
    sbuf.sem_op = 1; // V操作
    sbuf.sem_flg = SEM_UNDO;

    if (semop(sem_id, &sbuf, 1) == -1) {
        perror("V operation Error");
        return -1;
    }
    return 0;
}


int main(int argc, char **argv) {
    key_t key;
    int shmid, semid, msqid;
    char *shm;
    struct msg_from msg;   /* 消息队列用于通知对方更新了共享内存 */
    int flag = 1; /* while循环条件 */

    // 获取key值
    if ((key = ftok(".", 'z')) < 0) {
        perror("ftok error");
        exit(1);
    }

    // 创建共享内存
    if ((shmid = shmget(key, 1024, 0)) == -1) {
        perror("shmget error");
        exit(1);
    }

    // 连结共享内存
    shm = (char *)shmat(shmid, 0, 0);
    if ((int)shm == -1) {
        perror("Attach Shared Memory Error");
        exit(1);
    }

    // 创建消息队列
    if ((msqid = msgget(key, 0)) == -1) {
        perror("msgget error");
        exit(1);
    }

    // 获取信号量
	if ((semid = semget(key, 0, 0)) == -1) {
        perror("semget error");
        exit(1);
    }

    // 写数据
    printf("***************************************\n");
    printf("*                IPC                  *\n");
    printf("*    Input r to send data to server.  *\n");
    printf("*    Input q to quit.                 *\n");
    printf("***************************************\n");

    while(flag) {
        char c;
        printf("Please input command: ");
        scanf("%c", &c);
        switch (c) {
            case 'r':
                printf("Data to sent: ");
                sem_p(semid);
                scanf("%s", shm);
                sem_v(semid);
                /* 清空标准输入缓冲区 */
                while (((c = getchar()) != '\n') && (c != EOF));
                msg.mtype = 888;
                msg.mtext = 'r'; /* 发送消息通知服务端读取数据 */
                msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
                break;
		    case 'q':
                msg.mtype = 888;
                msg.mtext = 'q'; /* 发送消息通知服务端结束进程 */
                msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
                flag = 0;
                break;
		    default:
                printf("Wrong input!\n");
                /* 清空标准输入缓冲区 */
                while(((c = getchar()) != '\n') && (c != EOF));
        }
    }

    // 断开连结
    shmdt(shm);

    return 0;
}


